﻿// Decompiled with JetBrains decompiler
// Type: Microsoft.InfoCards.SecondaryIndex
// Assembly: infocard, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// MVID: 8E14765A-6610-409A-BA36-099A0642905D
// Assembly location: E:\git\ALLIDA\windll\infocard.exe

using Microsoft.InfoCards.Diagnostics;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Microsoft.InfoCards
{
  internal class SecondaryIndex
  {
    private byte[] m_buffer;
    private int m_lastIndex;
    private IComparer<IntPtr> m_searchComparer;
    private IComparer<IntPtr> m_sortComparer;
    private SecondaryIndexDefinition m_definition;
    private bool m_isOpen;

    public SecondaryIndex(
      SecondaryIndexDefinition definition,
      IComparer<IntPtr> search,
      IComparer<IntPtr> sort)
    {
      if (definition == null)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (definition));
      if (search == null)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (search));
      if (sort == null)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (sort));
      this.m_definition = definition;
      this.m_searchComparer = search;
      this.m_sortComparer = sort;
      this.Clear();
      this.m_isOpen = true;
    }

    public SecondaryIndexDefinition Definition
    {
      get
      {
        this.ThrowIfNotOpen();
        return this.m_definition;
      }
    }

    public int LastIndex
    {
      get
      {
        this.ThrowIfNotOpen();
        return this.m_lastIndex;
      }
    }

    public byte[] GetBuffer()
    {
      this.ThrowIfNotOpen();
      return this.m_buffer;
    }

    internal void SetValuesForId(int id, DataRowIndexBuffer indexBuffer, bool remove)
    {
      this.ThrowIfNotOpen();
      if (id <= 0)
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (id), SR.GetString("StoreLocalIdOutOfRange")));
      IndexObject[] array = indexBuffer[this.m_definition.Name].ToArray();
      if (remove)
        this.RemoveForIdAndShiftData(id);
      if (array == null || array.Length == 0)
        return;
      for (int index = 0; index < array.Length; ++index)
      {
        if (!array[index].IsCompiled)
        {
          if (!array[index].CanCompile)
            throw InfoCardTrace.ThrowHelperError((Exception) new InvalidOperationException(SR.GetString("StoreIndexObjectCanNotBeCompiled")));
          array[index].Compile(this.m_definition);
        }
        this.ValidateIndexValue(array[index].CompiledForm);
        this.ShiftAndInsertValues(id, array[index].CompiledForm);
      }
    }

    public void RemoveAllValuesForId(int id)
    {
      this.ThrowIfNotOpen();
      if (id <= 0)
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (id), SR.GetString("StoreLocalIdOutOfRange")));
      this.RemoveForIdAndShiftData(id);
    }

    public unsafe void PopulateRowIndexBuffer(DataRowIndexBuffer buffer, int id)
    {
      this.ThrowIfNotOpen();
      if (id <= 0)
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (id), SR.GetString("StoreLocalIdOutOfRange")));
      if (-1 == this.m_lastIndex)
        return;
      List<IndexObject> indexObjectList = new List<IndexObject>();
      fixed (byte* numPtr = &this.m_buffer[0])
      {
        for (int index = 0; index < this.m_lastIndex + 1; ++index)
        {
          if (((SecondaryIndexItem*) numPtr)[index].LocalId == id)
          {
            byte[] numArray = new byte[60];
            Marshal.Copy((IntPtr) ((void*) &((SecondaryIndexItem*) numPtr)[index].HashValue), numArray, 0, 60);
            indexObjectList.Add(new IndexObject(numArray));
          }
        }
        if (indexObjectList.Count > 0)
          buffer.SetIndexValues(this.m_definition.Name, indexObjectList.ToArray());
      }
    }

    public void Close()
    {
      if (!this.m_isOpen)
        return;
      this.Clear();
      this.m_buffer = (byte[]) null;
      this.m_isOpen = false;
    }

    public void ValidateIndexValue(byte[] indexValue)
    {
      if (SecondaryIndexSettings.Nullable != (this.m_definition.Settings & SecondaryIndexSettings.Nullable) && SecondaryIndex.IsBufferEmpty(indexValue))
        throw InfoCardTrace.ThrowHelperError((Exception) new InvalidOperationException(SR.GetString("StoreNullIndexValueNotPermitted", (object) this.m_definition.Name)));
    }

    public void SetBuffer(byte[] buffer, int lastIndex)
    {
      this.ThrowIfNotOpen();
      this.m_buffer = buffer;
      this.m_lastIndex = lastIndex;
    }

    public unsafe int Match(IndexObject obj, LocalIdCollection collection, int low, int high)
    {
      if (obj == null)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (obj));
      if (collection == null)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (collection));
      if ((uint) low > (uint) high)
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (low), (object) low, SR.GetString("StoreLowValueOutOfRange")));
      if ((uint) high > (uint) this.m_lastIndex)
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (high), (object) high, SR.GetString("StoreHighValueOutOfRange")));
      if (!obj.IsCompiled)
      {
        if (!obj.CanCompile)
          throw InfoCardTrace.ThrowHelperError((Exception) new InvalidOperationException(SR.GetString("StoreIndexObjectCanNotBeCompiled")));
        obj.Compile(this.m_definition);
      }
      this.ValidateIndexValue(obj.CompiledForm);
      SecondaryIndexItem secondaryIndexItem;
      Marshal.Copy(obj.CompiledForm, 0, (IntPtr) ((void*) &secondaryIndexItem.HashValue), 60);
      return this.Match(&secondaryIndexItem, collection, low, high);
    }

    public void Clear()
    {
      this.m_buffer = new byte[sizeof (SecondaryIndexItem) * this.m_definition.InitialSize];
      this.m_lastIndex = -1;
    }

    private unsafe void RemoveForIdAndShiftData(int id)
    {
      if (-1 == this.m_lastIndex)
        return;
      int num1 = this.m_lastIndex + 1;
      int num2 = 0;
      int index = 0;
      fixed (byte* numPtr = &this.m_buffer[0])
      {
        do
        {
          InfoCardTrace.Assert((uint) num2 < (uint) this.m_buffer.Length, "currentOffset is invalid or has been corrupted.");
          InfoCardTrace.Assert(0 == num2 % sizeof (SecondaryIndexItem), "currentOffset is not aligned proppertly.  This can cause data corruption.");
          InfoCardTrace.Assert((SecondaryIndexItem*) numPtr + index - (SecondaryIndexItem*) numPtr <= (long) (num1 * sizeof (SecondaryIndexItem)), "Current IndexPointer has walked beyond the total count of valid data.");
          index = num2 / sizeof (SecondaryIndexItem);
          InfoCardTrace.Assert((SecondaryIndexItem*) numPtr + index - (SecondaryIndexItem*) numPtr < (long) this.m_buffer.Length, "Current IndexPointer has walked beyond the allocated buffer");
          if (((SecondaryIndexItem*) numPtr)[index].LocalId == id)
          {
            if (num1 != index + 1)
            {
              InfoCardTrace.Assert(this.m_lastIndex >= 0, "LastIndex indicates an invalid state for the index");
              Array.Copy((Array) this.m_buffer, num2 + sizeof (SecondaryIndexItem), (Array) this.m_buffer, num2, (num1 - (index + 1)) * sizeof (SecondaryIndexItem));
              Array.Clear((Array) this.m_buffer, this.m_lastIndex * sizeof (SecondaryIndexItem), sizeof (SecondaryIndexItem));
            }
            else
              Array.Clear((Array) this.m_buffer, num2, sizeof (SecondaryIndexItem));
            --this.m_lastIndex;
            --num1;
          }
          else
            num2 += sizeof (SecondaryIndexItem);
        }
        while (num2 / sizeof (SecondaryIndexItem) < num1);
      }
    }

    private unsafe void ShiftAndInsertValues(int id, byte[] indexValue)
    {
      InfoCardTrace.Assert(id > 0, "Invalid LocalId");
      InfoCardTrace.Assert(null != indexValue, "Null indev value passed");
      InfoCardTrace.Assert(indexValue.Length == 60, "Index buffer is not correct size.");
      SecondaryIndexItem secondaryIndexItem;
      secondaryIndexItem.LocalId = id;
      Marshal.Copy(indexValue, 0, (IntPtr) ((void*) &secondaryIndexItem.HashValue), 60);
      this.AddItem(&secondaryIndexItem);
    }

    private static bool IsBufferEmpty(byte[] hash)
    {
      for (int index = 0; index < hash.Length; ++index)
      {
        if (hash[index] != (byte) 0)
          return false;
      }
      return true;
    }

    private unsafe int BSearch(
      SecondaryIndexItem* pMatch,
      IComparer<IntPtr> comp,
      int lowStart,
      int highStart)
    {
      int num1 = lowStart;
      int num2 = highStart;
      fixed (byte* numPtr = &this.m_buffer[0])
      {
        while (num1 <= num2)
        {
          int num3 = (num2 + num1) / 2;
          int num4 = comp.Compare((IntPtr) ((void*) pMatch), (IntPtr) ((void*) ((SecondaryIndexItem*) numPtr + num3)));
          if (num4 == 0)
          {
            num2 = num3;
            if (num2 == num1)
              return num2;
          }
          else if (num4 < 0)
            num2 = num3 - 1;
          else
            num1 = num3 + 1;
        }
      }
      return ~num1;
    }

    private unsafe int Match(
      SecondaryIndexItem* pMatch,
      LocalIdCollection results,
      int lowStart,
      int highStart)
    {
      int index = this.BSearch(pMatch, this.m_searchComparer, lowStart, highStart);
      if (index >= 0)
      {
        fixed (byte* numPtr = &this.m_buffer[0])
        {
          InfoCardTrace.Assert(index * sizeof (SecondaryIndexItem) < this.m_buffer.Length, "Index has moved beyond the allocated buffer.");
          do
          {
            results.Add(((SecondaryIndexItem*) numPtr)[index].LocalId);
            ++index;
          }
          while (index <= this.m_lastIndex && this.m_searchComparer.Compare((IntPtr) ((void*) ((SecondaryIndexItem*) numPtr + index)), (IntPtr) ((void*) pMatch)) == 0);
        }
        --index;
      }
      return index;
    }

    private unsafe void AddItem(SecondaryIndexItem* pMatch)
    {
      InfoCardTrace.Assert(IntPtr.Zero != (IntPtr) pMatch, "Match pointer is null");
      InfoCardTrace.Assert(pMatch->LocalId > 0, "Match pointer has invalid local id");
      int num1;
      if (SecondaryIndexSettings.Unique == (this.m_definition.Settings & SecondaryIndexSettings.Unique))
      {
        num1 = this.BSearch(pMatch, this.m_searchComparer, 0, this.m_lastIndex);
        if (num1 >= 0)
          throw InfoCardTrace.ThrowHelperError((Exception) new InvalidOperationException(SR.GetString("StoreUniqueIndexConstraintBroken", (object) this.m_definition.Name)));
      }
      else
        num1 = this.BSearch(pMatch, this.m_sortComparer, 0, this.m_lastIndex);
      if (num1 >= 0)
        return;
      int index = ~num1;
      int num2 = this.m_lastIndex + 1;
      int num3 = this.m_buffer.Length / sizeof (SecondaryIndexItem);
      if (num2 + 1 >= num3)
        this.GrowIndex();
      if (index < num2)
      {
        int num4 = num2 * sizeof (SecondaryIndexItem);
        int sourceIndex = index * sizeof (SecondaryIndexItem);
        int destinationIndex = sourceIndex + sizeof (SecondaryIndexItem);
        int length = num4 - sourceIndex;
        InfoCardTrace.Assert(length > 0, "No Bytes to copy into index.");
        Array.Copy((Array) this.m_buffer, sourceIndex, (Array) this.m_buffer, destinationIndex, length);
      }
      fixed (byte* numPtr = &this.m_buffer[0])
      {
        InfoCardTrace.Assert((SecondaryIndexItem*) numPtr + index - (SecondaryIndexItem*) numPtr < (long) this.m_buffer.Length, "IndexPointer is beyond the end of the allocated buffer");
        ((SecondaryIndexItem*) numPtr)[index] = *pMatch;
      }
      ++this.m_lastIndex;
    }

    private void GrowIndex()
    {
      int increaseByPercent = Utility.CalculateIncreaseByPercent(this.m_buffer.Length, sizeof (SecondaryIndexItem), 5);
      InfoCardTrace.Assert(increaseByPercent > this.m_buffer.Length && 0 == increaseByPercent % sizeof (SecondaryIndexItem), "New size calculated for index is invalid.");
      int length = (this.m_lastIndex + 1) * sizeof (SecondaryIndexItem);
      byte[] numArray = new byte[increaseByPercent];
      Array.Copy((Array) this.m_buffer, 0, (Array) numArray, 0, length);
      Array.Clear((Array) this.m_buffer, 0, length);
      this.m_buffer = numArray;
    }

    private void ThrowIfNotOpen()
    {
      if (!this.m_isOpen)
        throw InfoCardTrace.ThrowHelperError((Exception) new ObjectDisposedException(nameof (SecondaryIndex)));
    }
  }
}
